package main

import "fmt"
import "flag"
import "demoutil"
import "os"
import "strconv"
import "bufio"
import "io"

import "sort"
import "strings"
import "io/ioutil"

//import "math/rand"

// Distributed System
import "shardkv"

var ck *shardkv.Clerk
var f_login = flag.String("login", "anonymous", "log in name")
var f_smh = flag.String("masters", "", "port number")
var f_auto = flag.String("file", "", "port number")
var smh []string
var cmd demoutil.CMD

func main() {
	Init()
	if *f_auto == "" {
		//consolve version
		run := true
		line := ""
		in := bufio.NewReader(os.Stdin)
		var err error
		for run {
			fmt.Printf(">>>")
			// read space-seperated cmd into several arguments
			//fmt.Scanln(&line)
			line, err = in.ReadString('\n')
			if err != nil {
				if err == io.EOF {
					if line != "" {
						Execute(line)
					}
					run = false
				} else {
					fmt.Printf("error reading from stdout: %v\n", err)
					os.Exit(1)
				}
			} else {
				Execute(line)
				run = cmd.GetStatus()
			}
		}
	} else {
		f, err := os.Open(*f_auto)
		if err != nil {
			fmt.Printf("error opening file: %v\n", err)
			os.Exit(1)
		}
		in := bufio.NewReader(f)
		s, e := in.ReadString('\n')
		for e == nil {
			fmt.Printf(">>> %v", s)
			Execute(s)
			s, e = in.ReadString('\n')
		}
	}
}

func Init() {
	flag.Parse()
	//fmt.Println("Welcome, " + *f_login + " :) ")
	// command parser
	cmd = demoutil.Make_cmd()
	// set up server
	flag.Parse()
	smh := demoutil.ParseAddresses(*f_smh)
	//fmt.Println("Connected to masters: ", smh)
	ck = shardkv.MakeClerk(smh)

}
func Disp(fname string) {
	f, err := os.Open(fname)
	if err == nil {
		in := bufio.NewReader(f)
		s, e := in.ReadString('\n')
		for e == nil {
			fmt.Printf("%v", s)
			s, e = in.ReadString('\n')
		}
	} else {
		fmt.Println("No profile available")
	}
}
func Execute(line string) {
	execute := cmd.ParseInput(line)
	if execute {
		cc := cmd.GetTodo()
		err_msg := ""
		switch cc[0] {
		case "whoami":
			//hack it, instead of putting into log
			Disp("./im_" + *f_login)
		case "upload":
			if len(cc) == 1 {
				err_msg = "Need a command after upload"
			} else {
				content, err := ioutil.ReadFile(cc[1])
				if err != nil {
					err_msg = "Cannot upload file"
				} else {
					ck.Put(*f_login+"_"+cc[1], string(content))
				}
			}
		case "view":
			if len(cc) != 3 && len(cc) != 2 {
				err_msg = "usage: view [user] image"
			} else {
				recv_im := ""
				if len(cc) == 3 {
					recv := " " + *f_login + " " + ck.Get(*f_login+"_acl")
					//fmt.Println(recv,cc[1])
					if !strings.Contains(recv, " "+cc[1]+" ") {
						err_msg = cc[1] + " is not in ACL"
					} else {
						recv_im = ck.Get(cc[1] + "_" + cc[2])
					}
				} else {
					content, err := ioutil.ReadFile(cc[1])
					if err == nil {
						recv_im = string(content)
					}
				}
				if err_msg == "" {
					if recv_im == "" {
						err_msg = cc[2] + " is not available in " + cc[1]
					} else {
						fmt.Printf("%v", recv_im)
					}
				}
			}
		case "acl":
			// morris example Aceess Control List
			if *f_login == "anonymous" {
				err_msg = "Anonymous doesn't have ACL"
			} else {
				if len(cc) == 1 {
					err_msg = "Need a command after acl"
				} else {
					switch cc[1] {
					case "add":
						// need to change to puts
						if len(cc) > 2 {
							recv := ck.Get(*f_login + "_acl")
							ck.Put(*f_login+"_acl", recv+" "+strings.Join(cc[2:], " ")+" ")
						} else {
							err_msg = "need at least one argument to add to ACL"
						}
					case "del":
						// need to change to puts
						if len(cc) > 2 {
							recv := ck.Get(*f_login + "_acl")
							recv_s := strings.Split(strings.Trim(recv, " "), " ")
							for i := 0; i < len(cc)-2; i++ {
								if !strings.Contains(recv, " "+cc[i+2]+" ") {
									fmt.Printf("Can not delete %v which is not on the list\n", cc[i+2])
								} else {
									recv_s[sort.SearchStrings(recv_s, cc[i+2])] = ""
								}
							}
							ck.Put(*f_login+"_acl", " "+strings.Join(recv_s, " ")+" ")
						} else {
							err_msg = "need at least one argument to delete to ACL"
						}
					case "ls":
						recv := ck.Get(*f_login + "_acl")
						fmt.Printf("ACL :%v\n", recv)
					default:
						err_msg = "command not found"
					}
				}
			}
		case "put":
			if len(cc) != 3 {
				err_msg = "put: need two arguments, " + strconv.Itoa(len(cc)-1) + " provided"
			} else {
				ck.Put(cc[1], cc[2])
				//go ck.Put(cc[1],cc[2])
				fmt.Println("OK")
			}
		case "puts":
			if len(cc) < 3 {
				err_msg = "puts: need at least two arguments, " + strconv.Itoa(len(cc)-1) + " provided"
			} else {
				pairs := make([]shardkv.Pair, 0)
				for i := 1; i+1 < len(cc); i += 2 {
					pairs = append(pairs, shardkv.Pair{cc[i], cc[i+1]})
				}
				ck.Puts(pairs)
				//go ck.Put(cc[1],cc[2])
				fmt.Println("OK")
			}
		case "get":
			if len(cc) != 2 {
				err_msg = "get: need one argument, " + strconv.Itoa(len(cc)-1) + " provided"
			} else {
				recv := ck.Get(cc[1])
				//				fmt.Printf("%v\n", recv)
				fmt.Printf("Key: %v, Val: %v\n", cc[1], recv)
			}
		case "pause":
			ck.SetPauseTransaction(true)
			fmt.Println("OK")
		case "resume":
			ck.SetPauseTransaction(false)
			fmt.Println("OK")
		default:
			err_msg = "command not found"
		}
		if err_msg != "" {
			fmt.Println("Error:" + err_msg)
		}
	}
}
